// 基于信号量实现生产者和消费者模型

/*
    总资源数为1
    生产者有5个 -> 往链表头部添加节点
    消费者也有5个 -> 删除链表头部的节点。
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include<semaphore.h>

sem_t semp; // 生产者的信号量

sem_t semc; // 消费者的信号量

pthread_mutex_t mutex; // 互斥锁

// 链表的节点类型
struct Node
{
    int number;
    struct Node* next;
};

// 链表头节点
struct Node* head = NULL;

// 生产者线程函数
void* producer(void* arg)
{
    while(1)
    {
        sem_wait(&semp); // 检测生产者是否有资源
        pthread_mutex_lock(&mutex);
        // 创建新节点
        struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
        // init node
        newNode->number = rand() % 1000; // 0 ~ 999
        newNode->next = head;
        head = newNode; // 更新
        printf("producer, id:%ld, number: %d\n", pthread_self(), newNode->number);
        pthread_mutex_unlock(&mutex);
        sem_post(&semc); // “告诉”消费者有资源了！
        sleep(rand() % 3); 
    }
    return NULL;
}

// 消费者线程函数
void* consumer(void* arg)
{
    while(1)
    {
        sem_wait(&semc); // 检测 -> 没有资源则阻塞
        pthread_mutex_lock(&mutex);
        struct Node* node = head;
        printf("consumer, id:%ld, number: %d\n", pthread_self(), node->number);
        // 删除节点
        head = head->next;
        free(node);
        pthread_mutex_unlock(&mutex);
        sem_post(&semp); // “告诉”生产者有资源了！
        sleep(rand() % 3); 
    }
    return NULL;
}

int main()
{
    // 初始化信号量和条件变量
    sem_init(&semp, 0, 5); // 生产者
    sem_init(&semc, 0, 0); // 消费者 -> 资源数量初始化为0，消费者线程启动就阻塞了
    /*
    - 如果生产者和消费者使用的信号量总资源数为1
    - 那么不会出现生产者线程和消费者线程同时访问共享资源的情况
    - 不管生产者和消费者线程有多少个
    - 它们都是顺序执行的
    - 总资源数为1时 -> 信号量可代替互斥锁！
    */
    /*
    - 总资源数 > 1时 -> 需要加锁保证访问临界区时线性执行！
    */
    pthread_mutex_init(&mutex, NULL);

    // 创建生产者和消费者线程
    pthread_t t1[5], t2[5];
    for(int i = 0; i < 5; ++i)
    {
        pthread_create(&t1[i], NULL, producer, NULL);
    }
    for(int i = 0; i < 5; ++i)
    {
        pthread_create(&t2[i], NULL, consumer, NULL);
    }

    // 释放线程资源
    for(int i = 0; i < 5; ++i)
    {
        pthread_join(t1[i], NULL);
        pthread_join(t2[i], NULL);
    }

    // 销毁
    sem_destroy(&semp);
    sem_destroy(&semc);
    pthread_mutex_destroy(&mutex);

    return 0;
}